

<!DOCTYPE html>
<html class="writer-html5" lang="en" >
<head>
  <meta charset="utf-8" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  
  <title>Cephx 认证协议详细阐述 &mdash; Ceph Documentation</title>
  

  
  <link rel="stylesheet" href="../../_static/ceph.css" type="text/css" />
  <link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
  <link rel="stylesheet" href="../../_static/graphviz.css" type="text/css" />
  <link rel="stylesheet" href="../../_static/css/custom.css" type="text/css" />

  
  
    <link rel="shortcut icon" href="../../_static/favicon.ico"/>
  

  
  

  

  
  <!--[if lt IE 9]>
    <script src="../../_static/js/html5shiv.min.js"></script>
  <![endif]-->
  
    
      <script type="text/javascript" id="documentation_options" data-url_root="../../" src="../../_static/documentation_options.js"></script>
        <script src="../../_static/jquery.js"></script>
        <script src="../../_static/underscore.js"></script>
        <script src="../../_static/doctools.js"></script>
    
    <script type="text/javascript" src="../../_static/js/theme.js"></script>

    
    <link rel="index" title="Index" href="../../genindex/" />
    <link rel="search" title="Search" href="../../search/" />
    <link rel="next" title="配置管理系统" href="../config/" />
    <link rel="prev" title="Cephx" href="../cephx/" /> 
</head>

<body class="wy-body-for-nav">

   
  <header class="top-bar">
    

















<div role="navigation" aria-label="breadcrumbs navigation">

  <ul class="wy-breadcrumbs">
    
      <li><a href="../../" class="icon icon-home"></a> &raquo;</li>
        
          <li><a href="../internals/">Ceph 内幕</a> &raquo;</li>
        
      <li>Cephx 认证协议详细阐述</li>
    
    
      <li class="wy-breadcrumbs-aside">
        
          
            <a href="../../_sources/dev/cephx_protocol.rst.txt" rel="nofollow"> View page source</a>
          
        
      </li>
    
  </ul>

  
  <hr/>
</div>
  </header>
  <div class="wy-grid-for-nav">
    
    <nav data-toggle="wy-nav-shift" class="wy-nav-side">
      <div class="wy-side-scroll">
        <div class="wy-side-nav-search"  style="background: #eee" >
          

          
            <a href="../../">
          

          
            
            <img src="../../_static/logo.png" class="logo" alt="Logo"/>
          
          </a>

          

          
<div role="search">
  <form id="rtd-search-form" class="wy-form" action="../../search/" method="get">
    <input type="text" name="q" placeholder="Search docs" />
    <input type="hidden" name="check_keywords" value="yes" />
    <input type="hidden" name="area" value="default" />
  </form>
</div>

          
        </div>

        
        <div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
          
            
            
              
            
            
              <ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../../start/intro/">Ceph 简介</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../install/">安装 Ceph</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cephadm/">Cephadm</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../rados/">Ceph 存储集群</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../cephfs/">Ceph 文件系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../rbd/">Ceph 块设备</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../radosgw/">Ceph 对象网关</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../mgr/">Ceph 管理器守护进程</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../mgr/dashboard/">Ceph 仪表盘</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../api/">API 文档</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../architecture/">体系结构</a></li>
<li class="toctree-l1"><a class="reference internal" href="../developer_guide/">开发者指南</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="../internals/">Ceph 内幕</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="../blkin/">Tracing Ceph With LTTng</a></li>
<li class="toctree-l2"><a class="reference internal" href="../blkin/#tracing-ceph-with-blkin">Tracing Ceph With Blkin</a></li>
<li class="toctree-l2"><a class="reference internal" href="../bluestore/">BlueStore Internals</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cache-pool/">Cache pool</a></li>
<li class="toctree-l2"><a class="reference internal" href="../ceph_krb_auth/">如何配置好 Ceph Kerberos 认证的详细文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cephfs-mirroring/">CephFS Mirroring</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cephfs-reclaim/">CephFS Reclaim Interface</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cephfs-snapshots/">CephFS 快照</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cephx/">Cephx</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">Cephx 认证协议详细阐述</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#id1">简介</a></li>
<li class="toctree-l3"><a class="reference internal" href="#getting-started-with-authorization">Getting Started With Authorization</a></li>
<li class="toctree-l3"><a class="reference internal" href="#phase-i">Phase I:</a></li>
<li class="toctree-l3"><a class="reference internal" href="#phase-ii">Phase II</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="../config/">配置管理系统</a></li>
<li class="toctree-l2"><a class="reference internal" href="../config-key/">config-key layout</a></li>
<li class="toctree-l2"><a class="reference internal" href="../context/">CephContext</a></li>
<li class="toctree-l2"><a class="reference internal" href="../continuous-integration/">Continuous Integration Architecture</a></li>
<li class="toctree-l2"><a class="reference internal" href="../corpus/">资料库结构</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cpu-profiler/">Oprofile 的安装</a></li>
<li class="toctree-l2"><a class="reference internal" href="../cxx/">C++17 and libstdc++ ABI</a></li>
<li class="toctree-l2"><a class="reference internal" href="../deduplication/">去重</a></li>
<li class="toctree-l2"><a class="reference internal" href="../delayed-delete/">CephFS delayed deletion</a></li>
<li class="toctree-l2"><a class="reference internal" href="../dev_cluster_deployement/">开发集群的部署</a></li>
<li class="toctree-l2"><a class="reference internal" href="../dev_cluster_deployement/#id5">在同一机器上部署多套开发集群</a></li>
<li class="toctree-l2"><a class="reference internal" href="../development-workflow/">开发流程</a></li>
<li class="toctree-l2"><a class="reference internal" href="../documenting/">为 Ceph 写作文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../encoding/">序列化（编码、解码）</a></li>
<li class="toctree-l2"><a class="reference internal" href="../erasure-coded-pool/">纠删码存储池</a></li>
<li class="toctree-l2"><a class="reference internal" href="../file-striping/">File striping</a></li>
<li class="toctree-l2"><a class="reference internal" href="../freebsd/">FreeBSD Implementation details</a></li>
<li class="toctree-l2"><a class="reference internal" href="../generatedocs/">Ceph 文档的构建</a></li>
<li class="toctree-l2"><a class="reference internal" href="../health-reports/">Health Reports</a></li>
<li class="toctree-l2"><a class="reference internal" href="../iana/">IANA 号</a></li>
<li class="toctree-l2"><a class="reference internal" href="../kubernetes/">Hacking on Ceph in Kubernetes with Rook</a></li>
<li class="toctree-l2"><a class="reference internal" href="../libs/">库体系结构</a></li>
<li class="toctree-l2"><a class="reference internal" href="../logging/">集群日志的用法</a></li>
<li class="toctree-l2"><a class="reference internal" href="../logs/">调试日志</a></li>
<li class="toctree-l2"><a class="reference internal" href="../macos/">在 MacOS 上构建</a></li>
<li class="toctree-l2"><a class="reference internal" href="../messenger/">Messenger notes</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mon-bootstrap/">Monitor bootstrap</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mon-elections/">Monitor Elections</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mon-on-disk-formats/">ON-DISK FORMAT</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mon-osdmap-prune/">FULL OSDMAP VERSION PRUNING</a></li>
<li class="toctree-l2"><a class="reference internal" href="../msgr2/">msgr2 协议（ msgr2.0 和 msgr2.1 ）</a></li>
<li class="toctree-l2"><a class="reference internal" href="../network-encoding/">Network Encoding</a></li>
<li class="toctree-l2"><a class="reference internal" href="../network-protocol/">网络协议</a></li>
<li class="toctree-l2"><a class="reference internal" href="../object-store/">对象存储架构概述</a></li>
<li class="toctree-l2"><a class="reference internal" href="../osd-class-path/">OSD class path issues</a></li>
<li class="toctree-l2"><a class="reference internal" href="../peering/">互联</a></li>
<li class="toctree-l2"><a class="reference internal" href="../perf/">Using perf</a></li>
<li class="toctree-l2"><a class="reference internal" href="../perf_counters/">性能计数器</a></li>
<li class="toctree-l2"><a class="reference internal" href="../perf_histograms/">Perf histograms</a></li>
<li class="toctree-l2"><a class="reference internal" href="../placement-group/">PG （归置组）说明</a></li>
<li class="toctree-l2"><a class="reference internal" href="../quick_guide/">开发者指南（快速）</a></li>
<li class="toctree-l2"><a class="reference internal" href="../rados-client-protocol/">RADOS 客户端协议</a></li>
<li class="toctree-l2"><a class="reference internal" href="../rbd-diff/">RBD 增量备份</a></li>
<li class="toctree-l2"><a class="reference internal" href="../rbd-export/">RBD Export &amp; Import</a></li>
<li class="toctree-l2"><a class="reference internal" href="../rbd-layering/">RBD Layering</a></li>
<li class="toctree-l2"><a class="reference internal" href="../release-checklists/">Release checklists</a></li>
<li class="toctree-l2"><a class="reference internal" href="../release-process/">Ceph Release Process</a></li>
<li class="toctree-l2"><a class="reference internal" href="../seastore/">SeaStore</a></li>
<li class="toctree-l2"><a class="reference internal" href="../sepia/">Sepia 社区测试实验室</a></li>
<li class="toctree-l2"><a class="reference internal" href="../session_authentication/">Session Authentication for the Cephx Protocol</a></li>
<li class="toctree-l2"><a class="reference internal" href="../testing/">测试笔记</a></li>
<li class="toctree-l2"><a class="reference internal" href="../versions/">Public OSD Version</a></li>
<li class="toctree-l2"><a class="reference internal" href="../vstart-ganesha/">NFS CephFS-RGW Developer Guide</a></li>
<li class="toctree-l2"><a class="reference internal" href="../wireshark/">Wireshark Dissector</a></li>
<li class="toctree-l2"><a class="reference internal" href="../zoned-storage/">Zoned Storage Support</a></li>
<li class="toctree-l2"><a class="reference internal" href="../osd_internals/">OSD 开发者文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../mds_internals/">MDS 开发者文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../radosgw/">RADOS 网关开发者文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../ceph-volume/">ceph-volume 开发者文档</a></li>
<li class="toctree-l2"><a class="reference internal" href="../crimson/">Crimson developer documentation</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../../governance/">项目管理</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../foundation/">Ceph 基金会</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../ceph-volume/">ceph-volume</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../releases/general/">Ceph 版本（总目录）</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../releases/">Ceph 版本（索引）</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../security/">Security</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../glossary/">Ceph 术语</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../jaegertracing/">Tracing</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../translation_cn/">中文版翻译资源</a></li>
</ul>

            
          
        </div>
        
      </div>
    </nav>

    <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">

      
      <nav class="wy-nav-top" aria-label="top navigation">
        
          <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
          <a href="../../">Ceph</a>
        
      </nav>


      <div class="wy-nav-content">
        
        <div class="rst-content">
        
          <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
           <div itemprop="articleBody">
            
<div id="dev-warning" class="admonition note">
  <p class="first admonition-title">Notice</p>
  <p class="last">This document is for a development version of Ceph.</p>
</div>
  <div id="docubetter" align="right" style="padding: 5px; font-weight: bold;">
    <a href="https://pad.ceph.com/p/Report_Documentation_Bugs">Report a Documentation Bug</a>
  </div>

  
  <div class="section" id="cephx">
<span id="cephx-2012-peter"></span><h1>Cephx 认证协议详细阐述<a class="headerlink" href="#cephx" title="Permalink to this headline">¶</a></h1>
<p>Peter Reiher
7/13/12</p>
<p>This document provides deeper detail on the Cephx authorization protocol whose high level flow
is described in the memo by Yehuda (12/19/09).  Because this memo discusses details of
routines called and variables used, it represents a snapshot.  The code might be changed
subsequent to the creation of this document, and the document is not likely to be updated in
lockstep.  With luck, code comments will indicate major changes in the way the protocol is
implemented.</p>
<div class="section" id="id1">
<h2>简介<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h2>
<p>The basic idea of the protocol is based on Kerberos.  A client wishes to obtain something from
a server.  The server will only offer the requested service to authorized clients.  Rather
than requiring each server to deal with authentication and authorization issues, the system
uses an authorization server.  Thus, the client must first communicate with the authorization
server to authenticate itself and to obtain credentials that will grant it access to the
service it wants.</p>
<p>Authorization is not the same as authentication.  Authentication provides evidence that some
party is who it claims to be.  Authorization provides evidence that a particular party is
allowed to do something.  Generally, secure authorization implies secure authentication
(since without authentication, you may authorize something for an imposter), but the reverse
is not necessarily true.  One can authenticate without authorizing.  The purpose
of this protocol is to authorize.</p>
<p>The basic approach is to use symmetric cryptography throughout.  Each client C has its own
secret key, known only to itself and the authorization server A.  Each server S has its own
secret key, known only to itself and the authorization server A.  Authorization information
will be passed in tickets, encrypted with the secret key of the entity that offers the service.
There will be a ticket that A gives to C, which permits C to ask A for other tickets.  This
ticket will be encrypted with A’s key, since A is the one who needs to check it.  There will
later be tickets that A issues that allow C to communicate with S to ask for service.  These
tickets will be encrypted with S’s key, since S needs to check them.   Since we wish to provide
security of the communications, as well, session keys are set up along with the tickets.
Currently, those session keys are only used for authentication purposes during this protocol
and the handshake between the client C and the server S, when the client provides its service
ticket.  They could be used for authentication or secrecy throughout, with some changes to
the system.</p>
<p>Several parties need to prove something to each other if this protocol is to achieve its
desired security effects.</p>
<p>1.  The client C must prove to the authenticator A that it really is C.  Since everything
is being done via messages, the client must also prove that the message proving authenticity
is fresh, and is not being replayed by an attacker.</p>
<p>2.  The authenticator A must prove to client C that it really is the authenticator.  Again,
proof that replay is not occurring is also required.</p>
<p>3.  A and C must securely share a session key to be used for distribution of later
authorization material between them.  Again, no replay is allowable, and the key must be
known only to A and C.</p>
<p>4.  A must receive evidence from C that allows A to look up C’s authorized operations with
server S.</p>
<p>5.  C must receive a ticket from A that will prove to S that C can perform its authorized
operations.   This ticket must be usable only by C.</p>
<p>6.  C must receive from A a session key to protect the communications between C and S.  The
session key must be fresh and not the result of a replay.</p>
</div>
<div class="section" id="getting-started-with-authorization">
<h2>Getting Started With Authorization<a class="headerlink" href="#getting-started-with-authorization" title="Permalink to this headline">¶</a></h2>
<p>When the client first needs to get service, it contacts the monitor.  At the moment, it has
no tickets.  Therefore, it uses the “unknown” protocol to talk to the monitor.  This protocol
is specified as <code class="docutils literal notranslate"><span class="pre">CEPH_AUTH_UNKNOWN</span></code>.  The monitor also takes on the authentication server
role, A.  The remainder of the communications will use the cephx protocol (most of whose code
will be found in files in <code class="docutils literal notranslate"><span class="pre">auth/cephx</span></code>).  This protocol is responsible for creating and
communicating the tickets spoken of above.</p>
<p>Currently, this document does not follow the pre-cephx protocol flow.  It starts up at the
point where the client has contacted the server and is ready to start the cephx protocol itself.</p>
<p>Once we are in the cephx protocol, we can get the tickets.  First, C needs a ticket that
allows secure communications with A.  This ticket can then be used to obtain other tickets.
This is phase I of the protocol, and consists of a send from C to A and a response from A to C.
Then, C needs a ticket to allow it to talk to S to get services.  This is phase II of the
protocol, and consists of a send from C to A and a response from A to C.</p>
</div>
<div class="section" id="phase-i">
<h2>Phase I:<a class="headerlink" href="#phase-i" title="Permalink to this headline">¶</a></h2>
<p>The client is set up to know that it needs certain things, using a variable called <code class="docutils literal notranslate"><span class="pre">need</span></code>,
which is part of the <code class="docutils literal notranslate"><span class="pre">AuthClientHandler</span></code> class, which the <code class="docutils literal notranslate"><span class="pre">CephxClientHandler</span></code> inherits
from.  At this point, one thing that’s encoded in the <code class="docutils literal notranslate"><span class="pre">need</span></code> variable is
<code class="docutils literal notranslate"><span class="pre">CEPH_ENTITY_TYPE_AUTH</span></code>, indicating that we need to start the authentication protocol
from scratch.  Since we’re always talking to the same authorization server, if we’ve gone
through this step of the protocol before (and the resulting ticket/session hasn’t timed out),
we can skip this step and just ask for client tickets.  But it must be done initially, and
we’ll assume that we are in that state.</p>
<p>The message C sends to A in phase I is build in <code class="docutils literal notranslate"><span class="pre">CephxClientHandler::build_request()</span></code> (in
<code class="docutils literal notranslate"><span class="pre">auth/cephx/CephxClientHandler.cc</span></code>).  This routine is used for more than one purpose.
In this case, we first call <code class="docutils literal notranslate"><span class="pre">validate_tickets()</span></code> (from routine
<code class="docutils literal notranslate"><span class="pre">CephXTicektManager::validate_tickets()</span></code> which lives in <code class="docutils literal notranslate"><span class="pre">auth/cephx/CephxProtocol.h</span></code>).
This code runs through the list of possible tickets to determine what we need, setting values
in the <code class="docutils literal notranslate"><span class="pre">need</span></code> flag as necessary.  Then we call <code class="docutils literal notranslate"><span class="pre">ticket.get_handler()</span></code>.  This routine
(in <code class="docutils literal notranslate"><span class="pre">CephxProtocol.h</span></code>) finds a ticket of the specified type (a ticket to perform
authorization) in the ticket map, creates a ticket handler object for it,  and puts the
handler into the right place in the map.  Then we hit specialized code to deal with individual
cases.  The case here is when we still need to authenticate to A (the
<code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">(need</span> <span class="pre">&amp;</span> <span class="pre">CEPH_ENTITY_TYPE_AUTH)</span></code> branch).</p>
<p>We now create a message of type <code class="docutils literal notranslate"><span class="pre">CEPHX_GET_AUTH_SESSION_KEY</span></code>.  We need to authenticate
this message with C’s secret key, so we fetch that from the local key repository.  We create
a random challenge, whose purpose is to prevent replays.  We encrypt that challenge using
<code class="docutils literal notranslate"><span class="pre">cephx_calc_client_server_challenge()</span></code>.  We already
have a server challenge (a similar set of random bytes, but created by the server and sent to
the client) from our pre-cephx stage.  We take both challenges and our secret key and
produce a combined encrypted challenge value, which goes into <code class="docutils literal notranslate"><span class="pre">req.key</span></code>.</p>
<p>If we have an old ticket, we store it in <code class="docutils literal notranslate"><span class="pre">req.old_ticket</span></code>.  We’re about to get a new one.</p>
<p>The entire <code class="docutils literal notranslate"><span class="pre">req</span></code> structure, including the old ticket and the cryptographic hash of the two
challenges, gets put into the message.  Then we return from this function, and the
message is sent.</p>
<p>We now switch over to the authenticator side, A.  The server receives the message that was
sent, of type <code class="docutils literal notranslate"><span class="pre">CEPH_GET_AUTH_SESSION_KEY</span></code>.  The message gets handled in <code class="docutils literal notranslate"><span class="pre">prep_auth()</span></code>,
in <code class="docutils literal notranslate"><span class="pre">mon/AuthMonitor.cc</span></code>, which calls <code class="docutils literal notranslate"><span class="pre">handle_request()</span></code> is <code class="docutils literal notranslate"><span class="pre">CephxServiceHandler.cc</span></code> to
do most of the work.  This routine, also, handles multiple cases.</p>
<p>The control flow is determined by the <code class="docutils literal notranslate"><span class="pre">request_type</span></code> in the <code class="docutils literal notranslate"><span class="pre">cephx_header</span></code> associated
with the message.  Our case here is <code class="docutils literal notranslate"><span class="pre">CEPH_GET_AUTH_SESSION_KEY</span></code>.  We need the
secret key A shares with C, so we call <code class="docutils literal notranslate"><span class="pre">get_secret()</span></code> from out local key repository to get
it. (It’s called a <code class="docutils literal notranslate"><span class="pre">key_server</span></code> in the code, but it’s not really a separate machine or
processing entity. It’s more like the place where locally used keys are kept.)  We should
have set up a server challenge already with this client, so we make sure
we really do have one.  (This variable is specific to a <code class="docutils literal notranslate"><span class="pre">CephxServiceHandler</span></code>, so there
is a different one for each such structure we create, presumably one per client A is
dealing with.)  If there is no challenge, we’ll need to start over, since we need to
check the client’s crypto hash, which depends on a server challenge, in part.</p>
<p>We now call the same routine the client used to calculate the hash, based on the same values:
the client challenge (which is in the incoming message), the server challenge (which we saved),
and the client’s key (which we just obtained).  We check to see if the client sent the same
thing we expected.  If so, we know we’re talking to the right client.  We know the session is
fresh, because it used the challenge we sent it to calculate its crypto hash.  So we can
give it an authentication ticket.</p>
<p>We fetch C’s <code class="docutils literal notranslate"><span class="pre">eauth</span></code> structure.  This contains an ID, a key, and a set of caps (capabilities).</p>
<p>The client sent us its old ticket in the message, if it had one.  If so, we set a flag,
<code class="docutils literal notranslate"><span class="pre">should_enc_ticket</span></code>, to true and set the global ID to the global ID in that old ticket.
If the attempt to decode its old ticket fails (most probably because it didn’t have one),
<code class="docutils literal notranslate"><span class="pre">should_enc_ticket</span></code> remains false.  Now we set up the new ticket, filling in timestamps,
the name of C, the global ID provided in the method call (unless there was an old ticket), and
his <code class="docutils literal notranslate"><span class="pre">auid</span></code>, obtained from the <code class="docutils literal notranslate"><span class="pre">eauth</span></code> structure obtained above.  We need a new session key
to help the client communicate securely with us, not using its permanent key.    We set the
service ID to <code class="docutils literal notranslate"><span class="pre">CEPH_ENTITY_TYPE_AUTH</span></code>, which will tell the client C what to do with the
message we send it.  We build a cephx response header and call
<code class="docutils literal notranslate"><span class="pre">cephx_build_service_ticket_reply()</span></code>.</p>
<p><code class="docutils literal notranslate"><span class="pre">cephx_build_service_ticket_reply()</span></code> is in <code class="docutils literal notranslate"><span class="pre">auth/cephx/CephxProtocol.cc</span></code>.  This
routine will build up the response message.   Much of it copies data from its parameters to
a message structure.  Part of that information (the session key and the validity period)
gets encrypted with C’s permanent key.  If the <code class="docutils literal notranslate"><span class="pre">should_encrypt_ticket</span></code> flag is set,
encrypt it using the old ticket’s key.  Otherwise, there was no old ticket key, so the
new ticket is not encrypted.  (It is, of course, already encrypted with A’s permanent key.)
Presumably the point of this second encryption is to expose less material encrypted with
permanent keys.</p>
<p>Then we call the key server’s <code class="docutils literal notranslate"><span class="pre">get_service_caps()</span></code> routine on the entity name, with a
flag <code class="docutils literal notranslate"><span class="pre">CEPH_ENTITY_TYPE_MON</span></code>, and capabilities, which will be filled in by this routine.
The use of that constant flag means we’re going to get the client’s caps for A, not for some
other data server.  The ticket here is to access the authorizer A, not the service S.  The
result of this call is that the caps variable  (a parameter to the routine we’re in) is
filled in with the monitor capabilities that will allow C to  access A’s authorization services.</p>
<p><code class="docutils literal notranslate"><span class="pre">handle_request()</span></code> itself does not send the response message.  It builds up the
<code class="docutils literal notranslate"><span class="pre">result_bl</span></code>, which basically holds that message’s contents, and the capabilities structure,
but it doesn’t send the message.  We go back to <code class="docutils literal notranslate"><span class="pre">prep_auth()</span></code>, in <code class="docutils literal notranslate"><span class="pre">mon/AuthMonitor.cc</span></code>,
for that.    This routine does some fiddling around with the caps structure that just got
filled in.  There’s a global ID that comes up as a result of this fiddling that is put into
the reply message.  The reply message is built here (mostly from the <code class="docutils literal notranslate"><span class="pre">response_bl</span></code> buffer)
and sent off.</p>
<p>This completes Phase I of the protocol.  At this point, C has authenticated itself to A, and A has generated a new session key and ticket allowing C to obtain server tickets from A.</p>
</div>
<div class="section" id="phase-ii">
<h2>Phase II<a class="headerlink" href="#phase-ii" title="Permalink to this headline">¶</a></h2>
<p>This phase starts when C receives the message from A containing a new ticket and session key.
The goal of this phase is to provide C with a session key and ticket allowing it to
communicate with S.</p>
<p>The message A sent to C is dispatched to <code class="docutils literal notranslate"><span class="pre">build_request()</span></code> in <code class="docutils literal notranslate"><span class="pre">CephxClientHandler.cc</span></code>,
the same routine that was used early in Phase I to build the first message in the protocol.
This time, when <code class="docutils literal notranslate"><span class="pre">validate_tickets()</span></code> is called, the <code class="docutils literal notranslate"><span class="pre">need</span></code> variable will not contain
<code class="docutils literal notranslate"><span class="pre">CEPH_ENTITY_TYPE_AUTH</span></code>, so a different branch through the bulk of the routine will be
used.  This is the branch indicated by <code class="docutils literal notranslate"><span class="pre">if</span> <span class="pre">(need)</span></code>.  We have a ticket for the authorizer,
but we still need service tickets.</p>
<p>We must send another message to A to obtain the tickets (and session key) for the server
S.  We set the <code class="docutils literal notranslate"><span class="pre">request_type</span></code> of the message to <code class="docutils literal notranslate"><span class="pre">CEPHX_GET_PRINCIPAL_SESSION_KEY</span></code> and
call <code class="docutils literal notranslate"><span class="pre">ticket_handler.build_authorizer()</span></code> to obtain an authorizer.  This routine is in
<code class="docutils literal notranslate"><span class="pre">CephxProtocol.cc</span></code>.  We set the key for this authorizer to be the session key we just got
from A,and create a new nonce.  We put the global ID, the service ID, and the ticket into a
message buffer that is part of the authorizer.  Then we create a new <code class="docutils literal notranslate"><span class="pre">CephXAuthorize</span></code>
structure.  The nonce we just created goes there.  We encrypt this <code class="docutils literal notranslate"><span class="pre">CephXAuthorize</span></code>
structure with the current session key and stuff it into the authorizer’s buffer.  We
return the authorizer.</p>
<p>Back in <code class="docutils literal notranslate"><span class="pre">build_request()</span></code>, we take the part of the authorizer that was just built (its
buffer, not the session key or anything else) and shove it into the buffer we’re creating
for the message that will go to A.  Then we delete the authorizer.  We put the requirements
for what we want in <code class="docutils literal notranslate"><span class="pre">req.keys</span></code>, and we put <code class="docutils literal notranslate"><span class="pre">req</span></code> into the buffer.  Then we return, and
the message gets sent.</p>
<p>The authorizer A receives this message which is of type <code class="docutils literal notranslate"><span class="pre">CEPHX_GET_PRINCIPAL_SESSION_KEY</span></code>.
The message gets handled in <code class="docutils literal notranslate"><span class="pre">prep_auth()</span></code>, in <code class="docutils literal notranslate"><span class="pre">mon/AuthMonitor.cc</span></code>, which again calls
<code class="docutils literal notranslate"><span class="pre">handle_request()</span></code> in <code class="docutils literal notranslate"><span class="pre">CephxServiceHandler.cc</span></code> to do most of the work.</p>
<p>In this case, <code class="docutils literal notranslate"><span class="pre">handle_request()</span></code> will take the <code class="docutils literal notranslate"><span class="pre">CEPHX_GET_PRINCIPAL_SESSION_KEY</span></code> case.
It will call <code class="docutils literal notranslate"><span class="pre">cephx_verify_authorizer()</span></code> in <code class="docutils literal notranslate"><span class="pre">CephxProtocol.cc</span></code>.  Here, we will grab
a bunch of data out of the input buffer, including the global and service IDs and the ticket
for A.   The ticket contains a <code class="docutils literal notranslate"><span class="pre">secret_id</span></code>, indicating which key is being used for it.
If the secret ID pulled out of the ticket was -1, the ticket does not specify which secret
key A should use.  In this case, A should use the key for the specific entity that C wants
to contact, rather than a rotating key shared by all server entities of the same type.
To get that key, A must consult the key repository to find the right key.   Otherwise,
there’s already a structure obtained from the key repository to hold the necessary secret.
Server secrets rotate on a time expiration basis (key rotation is not covered in this
document), so run through that structure to find its current secret.  Either way, A now
knows the secret key used to create this ticket.  Now decrypt the encrypted part of the
ticket, using this key.  It should be a ticket for A.</p>
<p>The ticket also contains a session key that C should have used to encrypt other parts of
this message.  Use that session key to decrypt the rest of the message.</p>
<p>Create a <code class="docutils literal notranslate"><span class="pre">CephXAuthorizeReply</span></code> to hold our reply.  Extract the nonce (which was in the stuff
we just decrypted), add 1 to it, and put the result in the reply.  Encrypt the reply and
put it in the buffer provided in the call to <code class="docutils literal notranslate"><span class="pre">cephx_verify_authorizer()</span></code> and return
to <code class="docutils literal notranslate"><span class="pre">handle_request()</span></code>.  This will be used to prove to C that A (rather than an attacker)
created this response.</p>
<p>Having verified that the message is valid and from C, now we need to build it a ticket for S.
We need to know what S it wants to communicate with and what services it wants.  Pull the
ticket request that describes those things out of its message.  Now run through the ticket
request to see what it wanted.  (He could potentially be asking for multiple different
services in the same request, but we will assume it’s just one, for this discussion.)  Once we
know which service ID it’s after, call <code class="docutils literal notranslate"><span class="pre">build_session_auth_info()</span></code>.</p>
<p><code class="docutils literal notranslate"><span class="pre">build_session_auth_info()</span></code> is in <code class="docutils literal notranslate"><span class="pre">CephxKeyServer.cc</span></code>.  It checks to see if the
secret for the <code class="docutils literal notranslate"><span class="pre">service_ID</span></code> of S is available and puts it into the subfield of one of
the parameters, and calls the similarly named <code class="docutils literal notranslate"><span class="pre">_build_session_auth_info()</span></code>, located in
the same file.      This routine loads up the new <code class="docutils literal notranslate"><span class="pre">auth_info</span></code> structure with the
ID of S, a ticket, and some timestamps for that ticket.  It generates a new session key
and puts it in the structure.   It then calls <code class="docutils literal notranslate"><span class="pre">get_caps()</span></code> to fill in the
<code class="docutils literal notranslate"><span class="pre">info.ticket</span></code> caps field.  <code class="docutils literal notranslate"><span class="pre">get_caps()</span></code> is also in <code class="docutils literal notranslate"><span class="pre">CephxKeyServer.cc</span></code>.  It fills the
<code class="docutils literal notranslate"><span class="pre">caps_info</span></code> structure it is provided with caps for S allowed to C.</p>
<p>Once <code class="docutils literal notranslate"><span class="pre">build_session_auth_info()</span></code> returns, A has a list of the capabilities allowed to
C for S.  We put a validity period based on the current TTL for this context into the info
structure, and put it into the <code class="docutils literal notranslate"><span class="pre">info_vec</span></code> structure we are preparing in response to the
message.</p>
<p>Now call <code class="docutils literal notranslate"><span class="pre">build_cephx_response_header()</span></code>, also in <code class="docutils literal notranslate"><span class="pre">CephxServiceHandler.cc</span></code>.   Fill in
the <code class="docutils literal notranslate"><span class="pre">request_type</span></code>, which is <code class="docutils literal notranslate"><span class="pre">CEPHX_GET_PRINCIPAL_SESSION_KEY</span></code>, a status of 0,
and the result buffer.</p>
<p>Now call <code class="docutils literal notranslate"><span class="pre">cephx_build_service_ticket_reply()</span></code>, which is in <code class="docutils literal notranslate"><span class="pre">CephxProtocol.cc</span></code>.  The
same routine was used towards the end of A’s handling of its response in phase I.  Here,
the session key (now a session key to talk to S, not A) and the validity period for that
key will be encrypted with the existing session key shared between C and A.
The <code class="docutils literal notranslate"><span class="pre">should_encrypt_ticket</span></code> parameter is false here, and no key is provided for that
encryption.  The ticket in question, destined for S once C sends it there, is already
encrypted with S’s secret.  So, essentially, this routine will put ID information,
the encrypted session key, and the ticket allowing C to talk to S into the buffer to
be sent to C.</p>
<p>After this routine returns, we exit from <code class="docutils literal notranslate"><span class="pre">handle_request()</span></code>, going back to <code class="docutils literal notranslate"><span class="pre">prep_auth()</span></code>
and ultimately to the underlying message send code.</p>
<p>The client receives this message. The nonce is checked as the message passes through
<code class="docutils literal notranslate"><span class="pre">Pipe::connect()</span></code>, which is in <code class="docutils literal notranslate"><span class="pre">msg/SimpleMessager.cc</span></code>.  In a lengthy <code class="docutils literal notranslate"><span class="pre">while(1)</span></code> loop in
the middle of this routine, it gets an authorizer.  If the get was successful, eventually
it will call <code class="docutils literal notranslate"><span class="pre">verify_reply()</span></code>, which checks the nonce.  <code class="docutils literal notranslate"><span class="pre">connect()</span></code> never explicitly
checks to see if it got an authorizer, which would suggest that failure to provide an
authorizer would allow an attacker to skip checking of the nonce.  However, in many places,
if there is no authorizer, important connection fields will get set to zero, which will
ultimately cause the connection to fail to provide data.  It would be worth testing, but
it looks like failure to provide an authorizer, which contains the nonce, would not be helpful
to an attacker.</p>
<p>The message eventually makes its way through to <code class="docutils literal notranslate"><span class="pre">handle_response()</span></code>, in
<code class="docutils literal notranslate"><span class="pre">CephxClientHandler.cc</span></code>.    In this routine, we call <code class="docutils literal notranslate"><span class="pre">get_handler()</span></code> to get a ticket
handler to hold the ticket we have just received.  This routine is embedded in the definition
for a <code class="docutils literal notranslate"><span class="pre">CephXTicketManager</span></code> structure.  It takes a type (<code class="docutils literal notranslate"><span class="pre">CEPH_ENTITY_TYPE_AUTH</span></code>, in
this case) and looks through the <code class="docutils literal notranslate"><span class="pre">tickets_map</span></code> to find that type.  There should be one, and
it should have the session key of the session between C and A in its entry.  This key will
be used to decrypt the information provided by A, particularly the new session key allowing
C to talk to S.</p>
<p>We then call <code class="docutils literal notranslate"><span class="pre">verify_service_ticket_reply()</span></code>, in <code class="docutils literal notranslate"><span class="pre">CephxProtocol.cc</span></code>.  This routine
needs to determine if the ticket is OK and also obtain the session key associated with this
ticket.  It decrypts the encrypted portion of the message buffer, using the session key
shared with A.  This ticket was not encrypted (well, not twice - tickets are always encrypted,
but sometimes double encrypted, which this one isn’t).  So it can be stored in a service
ticket buffer directly.  We now grab the ticket out of that buffer.</p>
<p>The stuff we decrypted with the session key shared between C and A included the new session
key.  That’s our current session key for this ticket, so set it.  Check validity and
set the expiration times.  Now return true, if we got this far.</p>
<p>Back in <code class="docutils literal notranslate"><span class="pre">handle_response()</span></code>, we now call <code class="docutils literal notranslate"><span class="pre">validate_tickets()</span></code> to adjust what we think
we need, since we now have a ticket we didn’t have before.  If we’ve taken care of
everything we need, we’ll return 0.</p>
<p>This ends phase II of the protocol.  We have now successfully set up a ticket and session key
for client C to talk to server S.  S will know that C is who it claims to be, since A will
verify it.  C will know it is S it’s talking to, again because A verified it.  The only
copies of the session key for C and S to communicate were sent encrypted under the permanent
keys of C and S, respectively, so no other party (excepting A, who is trusted by all) knows
that session key.  The ticket will securely indicate to S what C is allowed to do, attested
to by A.  The nonces passed back and forth between A and C ensure that they have not been
subject to a replay attack.  C has not yet actually talked to S, but it is ready to.</p>
<p>Much of the security here falls apart if one of the permanent keys is compromised.  Compromise
of C’s key means that the attacker can pose as C and obtain all of C’s privileges, and can
eavesdrop on C’s legitimate conversations.  He can also pretend to be A, but only in
conversations with C.  Since it does not (by hypothesis) have keys for any services, he
cannot generate any new tickets for services, though it can replay old tickets and session
keys until S’s permanent key is changed or the old tickets time out.</p>
<p>Compromise of S’s key means that the attacker can pose as S to anyone, and can eavesdrop on
any user’s conversation with S.  Unless some client’s key is also compromised, the attacker
cannot generate new fake client tickets for S, since doing so requires it to authenticate
himself as A, using the client key it doesn’t know.</p>
</div>
</div>



           </div>
           
          </div>
          <footer>
    <div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
        <a href="../config/" class="btn btn-neutral float-right" title="配置管理系统" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
        <a href="../cephx/" class="btn btn-neutral float-left" title="Cephx" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
    </div>

  <hr/>

  <div role="contentinfo">
    <p>
        &#169; Copyright 2016, Ceph authors and contributors. Licensed under Creative Commons Attribution Share Alike 3.0 (CC-BY-SA-3.0).

    </p>
  </div> 

</footer>
        </div>
      </div>

    </section>

  </div>
  

  <script type="text/javascript">
      jQuery(function () {
          SphinxRtdTheme.Navigation.enable(true);
      });
  </script>

  
  
    
   

</body>
</html>